Go Web编程
======【Go与Web应用】======
使用Go语言构建Web应用
- 初始化模块文件 go.mod
go mod init go-web-programming- 创建第一个 Go Web 应用 main.go
package main
import (
"fmt"
"net/http"
)
func handler(writer http.ResponseWriter, request *http.Request) {
fmt.Fprintf(writer, "Hello World, %s!", request.URL.Path[1:])
}
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}- 编译并安装
go install go-web-programming- 运行 Go Web 服务
cd $GOPATH/bin
go-web-programming如果PATH环境变量没配,需要
export PATH=$PATH:$GOPATH/bin,然后运行source ~/.bash_profile或source ~/.zshrc来使更改生效。
- 访问URL
浏览器打开:localhost:8080/zhangsan
输出:
Hello World, zhangsan!
ChitChat论坛
在 Linux 或 FreeBSD 系统上安装
sudo apt-get install postgresql postgresql-contrib在 Windows 系统上安装
安装 EnterpriseDB 软件
连接数据库
createdb chitchat- 初始化数据库
psql -f setup.sql -d chitchat======【Web应用的基本组成部分】======
接收请求
GO语言拥有一系列成熟的标准库,如net/http和html/template,这些标准库可以用于构建Web应用。
Go的net/http标准库
Client、Response、Header、Request和Cookie对客户端进行支持;Server、ServerMux、Handler/HandleFunc、ResponseWriter、Header、Request和Cookie则对服务器进行支持。串联多个处理器和处理器函数
就像搭积木一样,既然我们可以串联起两个函数,那么自然也可以串联起更多函数。串联多个函数可以让程序执行更多动作,这种做法有时候也称为 管道处理(pipeline processing)
- ServeMux和DefaultServeMux
ServeMux是一个HTTP请求多路复用器,它负责接收HTTP请求并根据请求中的URL将请求重定向到正确的处理器。
ServeMux的一个缺陷是无法使用变量实现URL模式匹配。
最小惊讶原则
最小惊讶原则,也称最小意外原则,是设计包括软件在内的一切事物的一条通用规则,它指的是我们在进行设计的时候,应该做那些合乎常理的事情,使事物的行为总是显而易见、始终如一并且合乎情理。
使用HTTP/2
在1.6或以上版本,如果使用HTTPS模式启动服务器,那么服务器将默认使用HTTP/2
处理请求Request和ResponseWriter
请求和响应
HTTP报文是在客户端和服务器之间传递的消息,它分为HTTP请求和HTTP响应两种类型。
这两种类型的报文都拥有相同的结构:
(1) 请求行或者响应行 (2) 零个或多个首部 (3) 一个空行 (4) 一个可选的报文主体
如:
GET /Protocols/rfc2616.html HTTP/1.1
Host: www.w3.org
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36
(empty line)Request结构
Request结构主要由以下部分组成:
- URL字段
- Header字段
- Body字段
- Form字段、PostForm字段和MultipartForm字段
请求URL
URL的一般格式为:
scheme://[userinfo@]host/path[?query][#fragment]请求首部
h := r.Header
h := r.Heaer["Accept-Encoding"]
h := r.Header.Get("Accept-Encoding")
// 返回字符串形式的首部值,多个首部值将使用逗号分割。PostForm字段
PostForm字段只支持application/x-www-form-urlencoded编码
MultipartForm字段
func getCookie(w http.ResponseWriter, r *http.Request) {
r.ParseMultipartForm(1024)
fmt.Fprintln(w, r.MultipartForm)
}- FormValue方法允许直接访问与给定键相关联的值
与Form字段的唯一区别在于:因为FormValue方法在需要时会自动调用ParseForm方法或者ParseMultipartForm方法,所以用户在执行 FormValue方法之前,不需要手动调用上面提到的两个语法分析方法
r.FormValue("hello")
r.PostFormValue("hello")
r.PostForm("hello")文件
multipart/form-data编码通常用于实现文件上传功能
file, _, err := r.FormFile("file")
if err == nil {
data, err := ioutil.ReadAll(file)
if err == nil {
fmt.Fprintf(w, string(data))
}
}ResponseWriter
ResponseWriter看上去像是一个值,但它实际上却是一个带有结构指针的接口
ResponseWriter接口拥有以下3个方法:
- Write
- WriteHeader
- Header
对ResponseWriter进行写入
str := `<html>
<head><title>Go Web Programming</title></head>
<body><h1>Hello World</h1></body>
</html>
`
w.Write([]byte(str))- WriteHeader方法接受一个代表HTTP响应状态码的整数作为参数,并将这个整数用作HTTP响应的返回状态码;
w.WriteHeader(501)- 通过编写首部实现客户端重定向
// 顺序不能调换
w.Header().Set("Location", "https://www.xxx.com")
w.WriteHeader(302)- 编写JSON输出
w.Header().Set("Content-Type", "application/json")
post := &Post{
User: "Sau Sheong",
Threads: []string{"first", "second", "third"},
}
json, _ := json.Marshal(post)
w.Write(json)cookie
- 将cookie发送至浏览器
c1 := http.Cookie{
Name: "c1",
Value: "value1",
HttpOnly: true,
}
c2 := http.Cookie{
Name: "c2",
Value: "value2",
HttpOnly: true,
}
w.Header().Set("Set-Cookie", c1.String())
w.Header().Add("Set-Cookie", c2.String())
// 如下等同,更简洁的写法
http.SetCookie(w, &c1)
http.SetCookie(w, &c2)- 从请求的首部获取cookie
func getCookie(w http.ResponseWriter, r *http.Request) {
h := r.Header["Cookie"]
fmt.Println(h) // 返回一个切片[c1=value1; c2=value2]
}
// main函数绑定
http.HandleFunc("/getCookie", getCookie)- 使用cookie方法
c1, err := r.Cookie("c1")
if err != nil {
fmt.Fprintln(w, "Cookie not found")
}
fmt.Println(c1)- 使用cookies方法
cs := r.Cookies()
fmt.Fprintln(w, cs)
// 返回一个切片[c1=value1 c2=value2]内容展示template
模板引擎
Go的标准库通过text/template和html/template这两个库为模板提供了强有力的支持。
模板中的动作默认使用两个大括号包围。也可以自行指定其他定界符。
t, _ := template.ParseFiles("templates/index.html")
t.Execute(w, nil)对模板进行语法分析
ParseGlob会对匹配给定模式的所有文件进行语法分析。
如果目录里面只有tmpl.html一个HTML文件,那么语句
t, _ := template.ParseFiles("tmpl.html")和语句
t, _ := template.ParseGlob("*.html")将产生相同的效果。
- 手动处理模板错误
t := template.Must(template.ParseFiles("tmpl.html"))Must函数可以包裹起一个函数,被包裹的函数会返回一个指向模板的指针和一个错误,如果这个错误不是nil,那么Must函数将产生一个 panic。
执行模板
- 执行多个模板
t := template.ParseFiles("t1.html", "t2.html")- 指定模板传递变量
t, _ := template.ParseFiles("t1.html", "t2.html")
t.ExecuteTemplate(w, "t2.html", "Hello World")动作
Go模板的动作就是一些嵌入在模板里面的命令,这些命令在模板中使用两个大括号进行包围。
几种主要的动作:
- 条件动作
- 迭代动作
- 设置动作
- 包含动作
条件动作
- if条件
{{ if arg }}
some content
{{ end }}- if-else条件
{{ if arg }}
some content
{{ else }}
other content
{{ end }}迭代动作
迭代动作可以对数组、切片、映射或者通道进行迭代,而在迭代循环的内部,点(.)则会被设置为当前被迭代的元素。
{{ range array }}
item {{ . }}
{{ end }}
// 范例2
{{ range . }}
<li>{{ . }}</li>
{{ end }}设置动作
设置动作允许用户在指定的范围之内为点(.)设置值。
{{ with arg }}
Dot is set to arg
{{ end }}
// 带else
{{ with arg }}
Dot is set to arg
{{ else }}
Fallback if arg is empty
{{ end }}介于{ { with arg } }和{ { end } }之间的点将被设置为参数arg的值。
- 在设置点的时候提供备选方案
// go代码
t, _ := template.ParseFiles("tmpl.html")
t.Execute(w, "hello")
// tmpl.html代码
<p>is hello {{ . }}</p>
{{ with "" }}
<p>set to {{ . }}</p>
{{ else }}
<p>still hello {{ . }}</p>
{{ end }}
<p>still hello {{ . }}</p>
// 输出:
// is hello hello
// still hello hello
// still hello hello包含动作
包含动作允许用户在一个模板里面包含另一个模板,从而构建出嵌套的模板。
包含动作的格式为{ { template "name" } },其中name参数为被包含模板的名字。
{{ template "name" arg }} // arg就是用户想要传递给被嵌套模板的数据
// 比如将t1的点传递给t2
{{ template "t2.html" . }}参数、变量和管道
一个参数就是模板中的一个值
除了参数之外,用户还可以在动作中设置变量。变量以美元符号($)开头,就像这样:
$variable := value模板中的管道(pipeline)是多个有序地串联起来的参数、函数和方法。比如:
{{ p1 | p2 | p3 }}这里的p1、p2和p3可以是参数或者函数。管道允许用户将一个参数的输出传递给下一个参数,而各个参数之间则使用|分割。
{{ 12.3456 | printf "%.2f" }}函数
Go的模板引擎函数都是受限制的:尽管这些函数可以接受任意多个参数作为输入,但它们只能返回一个值,或者返回一个值和一个错误。
